<?php


/**
 * Przelicza wartość pojemności informatycznych na podane jednostki wyjściowe
 * @example $ma = new SizeCalculator(); echo $ma->countTo(1024,'kB',true);exit;
 * @version 1.0
 * @author Wookieb
 */
class SizeCalculator
{
	/**
	 * Tablica potęg do jakiej trzeba podnieść liczbę 1024 dla podanej jednostki
	 * @var array
	 */
	private static $dimensionsPow = array(
		'k' => 1,
		'm' => 2,
		'g' => 3,
		't' => 4,
		'p' => 5,
		'e' => 6
	);

	/**
	 * Domyślna jednostka wartości wejściowej
	 * @var string
	 */
	private static $defaultDimension = 'B';

	/**
	 * Jednostka wyjściowa
	 * @var string
	 */
	private static $outputDimension;

	/**
	 * Liczba miejsc po przecinku do których zaokrąglać wyniki
	 * @var int
	 */
	private static $roundPlaces = null;

	/**
	 * Podstawa
	 * @var int
	 */
	private static $divBase = 1024;
	
	/**
	 * Przelicza wartość na podaną jednostkę wyjściową.
	 * Wielkość litery b jest bardzo ważna. b = bit (0.125 bajta), B = bajt (8 bitów).
     * Wielkość drugiej litery jest ignorowana.
	 * @param string $size wartość wejściowa np 100kB
	 * @param string $outputDimension jednostka wyjściowa np. kB (domyślnie B),
	 * "auto" jeżeli wartość ma być automatycznie dobrana (automatyczna wartość operuje na bajtach)
	 * @param bool $addDimension Czy dodawać jednostkę wyjściową do wyniku
	 * @return int|bool wyliczona wartość, false jeżeli wartość wejściowa nie jest prawidłowa
	 */
	public static function countTo($size, $outputDimension = 'B', $addDimension = false)
	{	
		if(!preg_match('/([0-9,.]+)\s*([a-z]{1,2})?/i', $size, $matches))
		{
			trigger_error('Invalid input size', E_USER_WARNING);
			return false;
		}

		$tmpOutputDimension = trim((string)$outputDimension);
		$sizeNumber = (float)str_replace(',', '.', $matches[1]);
		$dimension	= (isset($matches[2]))?$matches[2]:self::$defaultDimension;
		
		// wartosc wejsciowa przeliczona na bajty/bity
		$div= self::getDivisor($dimension);
		$firstDiv = $sizeNumber * $div;
		// auto przeliczanie jednostki
		if(strtolower($outputDimension) == 'auto')
		{
			// tablica jednostek
			$dimensionsArray = array('B'=>0) + self::$dimensionsPow;

			// szukamy najblizszej wartosci
			foreach($dimensionsArray as $arrDimension=>$pow)
			{
				$outputDiv = (pow(self::$divBase, $pow) * 8);
				$result = ($firstDiv/$outputDiv);
				if( $result < self::$divBase)
				{
					$outputDimension = strtoupper($arrDimension);
					if($outputDimension != 'B') $outputDimension .= 'B';
					break;
				}
			}
		}
		else
		{
			$outputDiv = self::getDivisor($outputDimension);
			$result = ($firstDiv/$outputDiv);
		}
		
		// zaokraglanie
		if(self::$roundPlaces !== null)
		{
			$result = round($result, self::$roundPlaces);
		}
		
		self::$outputDimension = $outputDimension;
		return $result.(($addDimension)?' '.$outputDimension:'');
	}

	/**
	 * Zwraca liczbę reprezentującą wartość w bitach 1 podanej jednostki
	 * Np dla B zwroci 8
	 * @param string $dimension
	 * @return int
	 */
	public static function getDivisor($dimension)
	{
		// czy to bajty
		$isByte = true;
		if(substr($dimension, -1, 1) === 'b') $isByte = false;
		
		$dimension = $dimension[0];
		$dimension = strtolower($dimension);

		$val = 1;
		if( isset(self::$dimensionsPow[$dimension]) )
		{
			$val = pow(self::$divBase, self::$dimensionsPow[$dimension]);
		}
		if($isByte === true) $val *= 8;
		return $val;
	}

	/**
	 * Zwraca jednostkę wyjściową po ostatniej operacji przeliczenia
	 * @return string
	 */
	public static function getOutputDimension()
	{
		return self::$outputDimension;
	}

	/**
	 * Ustawia liczbę miejsc po przecinku do których zaokrąglać wynik
	 * @param int $places, null badz false jeżeli NIE zaokrąglać
	 */
	public static function setRoundPlaces($places)
	{
		if($places === null || $places === false) $places = null;
		else $places = round((int)$places, 0);
		
		self::$roundPlaces = $places;
	}
	
	/**
	 * Ustawia domyślną konfigurację kalkulatora
	 */
	public static function setToDefault()
	{
		self::$defaultDimension = 'B';
		self::$divBase = 1024;
		self::$roundPlaces = 2;
	}
	
	/**
	 * Zmienia podstawę dzielnika. Możliwe wartości to 1000 lub 1024
	 * @param int $base
	 * @return bool true jeżeli podstawa jest prawidłowa, false jeżeli nie
	 */
	public static function setBase($base)
	{
		$base = (int)$base;
		if($base == 1000 || $base == 1024)
		{
			self::$divBase = $base;
			return true;
		}

		trigger_error('Invalid division base', E_USER_NOTICE);
		return false;
	}
	
	/**
	 * Ustawia domyślną jednostkę wartości wejściowej
	 * @param string $dimension
	 * @return bool true w przyadpku poprawnej jednostki, false jeśli niepoprawna
	 */
	public static function setDefaultDimension($dimension)
	{
		$dimension = trim((string)$dimension);
		if(!preg_match('/^('.implode('|',array_keys(self::$dimensionsPow)).')b?$/i', $dimension))
		{
			trigger_error('Invalid default dimension', E_USER_NOTICE);
			return false;
		}
		
		self::$defaultDimension = $dimension;
		return true;
	}

}